<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<meta name="theme-color" content="#33474d">
	<title>深入理解PHP之：Nginx 与 FPM 的工作机制 | 失落的乐章</title>
	<link rel="stylesheet" href="/css/style.css" />
	
      <link rel="alternate" href="/atom.xml" title="失落的乐章" type="application/atom+xml">
    
</head>

<body>

	<header class="header">
		<nav class="header__nav">
			
				<a href="/archives" class="header__link">Archive</a>
			
				<a href="/tags" class="header__link">Tags</a>
			
				<a href="/atom.xml" class="header__link">RSS</a>
			
		</nav>
		<h1 class="header__title"><a href="/">失落的乐章</a></h1>
		<h2 class="header__subtitle">技术面前，永远都是学生。</h2>
	</header>

	<main>
		<article>
	
		<h1>深入理解PHP之：Nginx 与 FPM 的工作机制</h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LNMP/">LNMP</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;网络上有很多关于如何配置 Nginx + FPM 的文章，但它们更多从操作的角度出发，告诉我们怎么做，但却没有告诉我们为什么要这么做，本文从 Nginx 与 FPM 的工作机制出发，探讨配置背后的原理，让我们真正理解 Nginx 与 PHP 是如何协同工作的。要说 Nginx 与 PHP 是如何协同工作的，首先得说 CGI (Common Gateway Interface) 和 FastCGI 这两个协议。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;CGI 是 Web Server 与后台语言交互的协议，有了这个协议，开发者可以使用任何语言处理 Web Server 发来的请求，动态的生成内容。但 CGI 有一个致命的缺点，那就是每处理一个请求都需要 fork 一个全新的进程，随着 Web 的兴起，高并发越来越成为常态， 这样低效的方式明显不能满足需求。就这样，FastCGI 诞生了，CGI 很快就退出了历史的 舞台。FastCGI，顾名思义为更快的 CGI，它允许在一个进程内处理多个请求，而不是一个 请求处理完毕就直接结束进程，性能上有了很大的提高。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;至于 FPM (FastCGI Process Manager)，它是 FastCGI 的实现，任何实现了 FastCGI 协议的 Web Server 都能够与之通信。FPM 之于标准的 FastCGI，也提供了一些增强功能，具体可以参考官方文档：PHP: FPM Installation。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FPM 是一个 PHP 进程管理器，包含 master 进程和 worker 进程两种进程：master 进程只有一个，负责监听端口，接收来自 Web Server 的请求，而 worker 进程则一般有多个 (具体数量根据实际需要配置)，每个进程内部都嵌入了一个 PHP 解释器，是 PHP 代码真正执行的地方，下图是我本机上 fpm 的进程情况，1一个 master 进程，3个 worker 进程：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E4%B9%8B%EF%BC%9ANginx%20%E4%B8%8E%20FPM%20%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从 FPM 接收到请求，到处理完毕，其具体的流程如下：</p>
<ol>
<li>FPM 的 master 进程接收到请求</li>
<li>master 进程根据配置指派特定的 worker 进程进行请求处理，如果没有可用进程，返回错误，这也是我们配合 Nginx 遇到502错误比较多的原因。</li>
<li>worker 进程处理请求，如果超时，返回504错误</li>
<li>请求处理结束，返回结果</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FPM 从接收到处理请求的流程就是这样了，那么 Nginx 又是如何发送请求给 fpm 的呢？这就需要从 Nginx 层面来说明了。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们知道，Nginx 不仅仅是一个 Web 服务器，也是一个功能强大的 Proxy 服务器，除了进行 http 请求的代理，也可以进行许多其他协议请求的代理，包括本文与 fpm 相关的 fastcgi 协议。为了能够使 Nginx 理解 fastcgi 协议，Nginx 提供了 fastcgi 模块来将 http 请求映射为对应的 fastcgi 请求。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Nginx 的 fastcgi 模块提供了 fastcgi_param 指令来主要处理这些映射关系，下面 Ubuntu 下 Nginx 的一个配置文件，其主要完成的工作是将 Nginx 中的变量翻译成 PHP 中能够理解的变量。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E4%B9%8B%EF%BC%9ANginx%20%E4%B8%8E%20FPM%20%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;除此之外，非常重要的就是 fastcgi_pass 指令了，这个指令用于指定 fpm 进程监听的地址，Nginx 会把所有的 php 请求翻译成 fastcgi 请求之后再发送到这个地址。下面一个简单的可以工作的 Nginx 配置文件：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3PHP%E4%B9%8B%EF%BC%9ANginx%20%E4%B8%8E%20FPM%20%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在这个配置文件中，我们新建了一个虚拟主机，监听在 80 端口，Web 根目录为 /home/rf/projects/wordpress。然后我们通过 location 指令，将所有的以 .php 结尾的请求都交给 fastcgi 模块处理，从而把所有的 php 请求都交给了 fpm 处理，从而完成  Nginx 到 fpm 的闭环。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如此以来，Nginx 与 FPM 通信的整个流程应该比较清晰了吧。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;查看php-fpm.conf的配置文件</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">[root@192 etc]<span class="comment"># cat php-fpm.conf |grep -v '^;' |grep -v '^$' |grep -v "^ "</span></div><div class="line">[global]</div><div class="line">[www]</div><div class="line">user = php-fpm</div><div class="line">group = php-fpm</div><div class="line">listen = 127.0.0.1:9000</div><div class="line">pm = dynamic</div><div class="line">pm.max_children = 5</div><div class="line">pm.start_servers = 2</div><div class="line">pm.min_spare_servers = 1</div><div class="line">pm.max_spare_servers = 3</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;慢执行日志</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">slowlog = /path/to/slow.log</div><div class="line">request_slowlog_timeout = 1</div><div class="line"></div><div class="line">open_basedir</div><div class="line">php_admin_value[open_basedir]=/data/www/:/tmp/</div></pre></td></tr></table></figure>

	

	
		<span class="different-posts"><a href="/2017/10/12/Nginx/28. 深入理解PHP之：Nginx 与 FPM 的工作机制/" onclick="window.history.go(-1); return false;">⬅️ Go back </a></span>

	

</article>

	</main>

	<footer class="footer">
	<div class="footer-content">
		
	      <div class="footer__element">
	<p>Hi there, <br />welcome to my Blog glad you found it. Have a look around, will you?</p>
</div>

	    
	      <div class="footer__element">
	<h5>Check out</h5>
	<ul class="footer-links">
		<li class="footer-links__link"><a href="/archives">Archive</a></li>
		
		  <li class="footer-links__link"><a href="/atom.xml">RSS</a></li>
	    
		<li class="footer-links__link"><a href="/about">about page</a></li>
		<li class="footer-links__link"><a href="/tags">Tags</a></li>
		<li class="footer-links__link"><a href="/categories">Categories</a></li>
	</ul>
</div>

	    

		<div class="footer-credit">
			<span>© 2017 失落的乐章 | Powered by <a href="https://hexo.io/">Hexo</a> | Theme <a href="https://github.com/HoverBaum/meilidu-hexo">MeiliDu</a></span>
		</div>

	</div>


</footer>



</body>

</html>
